package com.safx.processor;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.xml.sax.SAXException;

import com.safx.xml.SchemaChecker;
import com.safx.entity.tags.TestCaseTag;
import com.safx.entity.tags.TestDefinitionTag;
import com.safx.entity.constants.XMLTestDefinitionTag;
import com.safx.entity.enums.ResultEnum;
import com.safx.entity.env.Environment;

/**
 * 生成报告
 * 
 * @author COOLOO
 * 
 */
public class ReportProcessor {
	Logger logger = Logger.getLogger(ReportProcessor.class);
	//html开头
	private static String htmlHead = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";

	/**
	 * 根据已经执行过的testcase对象
	 * <b>生成XML报告和html报告 </b>
	 * 
	 * @throws XMLStreamException
	 * @throws FileNotFoundException
	 * @throws UnsupportedEncodingException
	 * 
	 * @Author chenyue ( yue.chen2@hisoft.com ) Create on 2011-6-8
	 */
	public void XMLAndHTMLReport(List<TestDefinitionTag> tdList) {
		this.writeHtmlFile(this.XMLReport(tdList), "schema/Xml2Html.xsl");
	}
	
	/**
	 * 生成XML报告
	 * @param testcaseList
	 * @return
	 */
	private String XMLReport(List<TestDefinitionTag> tdList) {
		int pass = 0;
		int fail = 0;
		int skip = 0;
		long time = 0L;
		long totalTime = 0L;
		int totalPass = 0;
		int totalFail = 0;
		int totalSkip = 0;
		int casePassed = 0;
		SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd_HHmmss");
		String pathXml = "output/" + df.format(new Date()) + ".xml";
		logger.info("create XML report('" + pathXml + "').");
		Environment environment = Environment.getInstance();
		try {
			if (!(new File("~/output").isDirectory())) {
				new File("output").mkdir();
			}
		} catch (SecurityException e) {
			logger.error(e.getMessage(), e);
		}
		try {
			PrintWriter writerXml = new PrintWriter(new OutputStreamWriter(new FileOutputStream(pathXml), "utf-8"));
			XMLOutputFactory xof = XMLOutputFactory.newInstance();
			XMLStreamWriter xmlsw = xof.createXMLStreamWriter(writerXml);
			xmlsw.writeStartDocument("utf-8", "1.0");// write the statement of xml
			xmlsw.writeStartElement("Report");
			//write config environment information into xml file 
			this.Environment2XML(xmlsw, environment);
			xmlsw.writeStartElement("TestDefinitons");
			for(TestDefinitionTag td : tdList){
				ResultEnum re = null;
				xmlsw.writeStartElement("TestDefiniton");
				xmlsw.writeAttribute(XMLTestDefinitionTag.FILE_NAME, td.getFileName());
				xmlsw.writeAttribute(XMLTestDefinitionTag.TEST_DEFINITION_NAME, td.getAnnotationTag().getDescription());
//				xmlsw.writeAttribute(XMLTestCaseTag.TEST_CASE_VERSION, tc.getTestCaseVersion());
				xmlsw.writeAttribute(XMLTestDefinitionTag.AUTHOR, td.getAnnotationTag().getLastUpdatedAuthor());
				xmlsw.writeAttribute(XMLTestDefinitionTag.DATE, td.getAnnotationTag().getLastUpdatedDate());
				List<TestCaseTag> tcl = td.getTestsTag().getTestCaseTagList();
				for(TestCaseTag tc : tcl){
					xmlsw.writeStartElement("TestCase");
					re = tc.getResult();	
					xmlsw.writeAttribute(XMLTestDefinitionTag.LINENUMBER, ""+tc.getLineNumber());
					xmlsw.writeAttribute(XMLTestDefinitionTag.TEST_CASE_NAME, tc.getTestCaseName());
					xmlsw.writeAttribute(XMLTestDefinitionTag.TEST_CASE_DESCRIPTION,tc.getAnnotationTag().getDescription());
					xmlsw.writeAttribute(XMLTestDefinitionTag.RESULT, ""+tc.getResult());
					String comments = "";
					xmlsw.writeCharacters(comments);
					xmlsw.writeEndElement(); //TestCase Element End
					
					if (re.equals(ResultEnum.PASS)) {
						pass++;
					} else if (re.equals(ResultEnum.FAIL)) {
						fail++;
					} else {
						skip++;
					}
				}	// for (TestCaseTag tc : tcl)			
				time = td.getTime() / 1000;
				totalPass += pass;
				totalFail += fail;
				totalSkip += skip;
				totalTime += time;
				
				xmlsw.writeStartElement("TDResult");
				xmlsw.writeAttribute(XMLTestDefinitionTag.FILE_NAME, td.getFileName());
				xmlsw.writeAttribute(XMLTestDefinitionTag.PASSED, "" + pass);
				xmlsw.writeAttribute(XMLTestDefinitionTag.FAILED, "" + fail);
				xmlsw.writeAttribute(XMLTestDefinitionTag.SKIPED, "" + skip);
				xmlsw.writeAttribute(XMLTestDefinitionTag.TIME, "" + time);
				xmlsw.writeEndElement(); // TDResult  Element
				casePassed += pass / td.getTestsTag().getTestCaseTagList().size();
				pass = 0;
				fail = 0;
				skip = 0;
				time = 0L;
				
				xmlsw.writeEndElement(); // Test Definition Element
				xmlsw.writeEndElement(); // Test Definitions Element

			}// for(TestDefinitionTag td : tdList)
			xmlsw.writeStartElement("TestResult");
			xmlsw.writeAttribute(XMLTestDefinitionTag.TOTAL_PASS, "" + totalPass);
			xmlsw.writeAttribute(XMLTestDefinitionTag.TOTAL_FAIL, "" + totalFail);
			xmlsw.writeAttribute(XMLTestDefinitionTag.TOTAL_SKIP, "" + totalSkip);
			xmlsw.writeAttribute(XMLTestDefinitionTag.TOTAL_TIME, "" + totalTime);
			xmlsw.writeAttribute(XMLTestDefinitionTag.TOTAL_RESULT, "" + casePassed + " / " + tdList.size());
			xmlsw.writeEndElement(); // TestResult Element
			xmlsw.writeEndElement(); // Test Report Element
			xmlsw.flush();
			xmlsw.close();
			
		} catch (UnsupportedEncodingException e) {
			logger.error(e.getMessage(), e);
		} catch (FileNotFoundException e) {
			logger.error(e.getMessage(), e);
		} catch (FactoryConfigurationError e) {
			logger.error(e.getMessage(), e);
		} catch (XMLStreamException e) {
			logger.error(e.getMessage(), e);
		}
		formatXml(pathXml);
		return pathXml;
	}

/**
 * 格式化xml
 *
 * @param pathXml
 */
	public void formatXml(String pathXml) {
		logger.info("format XML report('" + pathXml + "').");
		try {
			SAXReader saxReader = new SAXReader();
			// 注释：先读入一个文件流
			Document doc = saxReader.read(new FileInputStream(new File(pathXml)));
			// 注释：创建输出格式
			OutputFormat format = OutputFormat.createPrettyPrint();
			// 注释：制定输出xml的编码类型
			format.setEncoding("UTF-8");
			// 注释：创建一个文件输出流
			XMLWriter writer = new XMLWriter(new FileWriter(new File(pathXml)), format);
			// 注释：将格式化后的xml串写入到文件
			writer.write(doc);
			writer.close();
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		}
	}

	/**
	 * 
	 * <b>转化xml到html</b>
	 * 
	 * @Author chenyue ( yue.chen2@hisoft.com ) Create on 2011-6-8
	 */
	public void writeHtmlFile(String xmlpath, String sxlpath) {
		File html = new File(xmlpath.replace(".xml", ".html"));
		logger.info("create HTML report('" + html.getPath() + "')");
		File xmlFile = new File(xmlpath);
		File sxlFile = new File(sxlpath);
		PrintWriter pw = null;
		StringWriter sw = null;
		if (SchemaChecker.fileExists(xmlFile) && SchemaChecker.fileExists(sxlFile) && SchemaChecker.validateOnlyXml(xmlFile)) {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			// 根据xml文件，取得相应的名字
			try {
				DocumentBuilder db = dbf.newDocumentBuilder();
				org.w3c.dom.Document doc = db.parse(xmlpath);
				DOMSource source = new DOMSource(doc);
				sw = new StringWriter();
				Result resulted = new StreamResult(sw);
				// StreamResult result = new StreamResult(html);
				StreamSource ss = new StreamSource(sxlpath);
				TransformerFactory tff = TransformerFactory.newInstance();
				Transformer tf = tff.newTransformer(ss);
				tf.setOutputProperty("encoding", "utf-8");// 设置编码格式为"UTF-8"
				// 转化
				tf.transform(source, resulted);
				// 生成html文件
				pw = new PrintWriter(html);
				// 替换"&gt;"为 ">".
				pw.write(htmlHead + sw.toString().replaceAll("&gt;", ">").replace("&lt;", "<").replace("&amp;", "&"));
			} catch (ParserConfigurationException e) {
				logger.error(e.getMessage(), e);
			} catch (SAXException e) {
				logger.error(e.getMessage(), e);
			} catch (IOException e) {
				logger.error(e.getMessage(), e);
			} catch (TransformerConfigurationException e) {
				logger.error(e.getMessage(), e);
			} catch (TransformerException e) {
				logger.error(e.getMessage(), e);
			} finally {
				try {
					if (!(pw == null)) {
						pw.close();
					}
					if (!(sw == null)) {
						sw.close();
					}
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
				}
			}
		}
	}
	/**
	 * 把environment转化为xml
	 *
	 * @param xmlsw
	 * @param environment
	 * @throws XMLStreamException
	 */
	private void Environment2XML(XMLStreamWriter xmlsw, Environment environment) throws XMLStreamException {

		SimpleDateFormat tempDate = new SimpleDateFormat("yyyy/MM/dd a", Locale.ENGLISH);
		String datetime = tempDate.format(new java.util.Date());
		xmlsw.writeStartElement("Configurations");
		xmlsw.writeStartElement("Time");
		xmlsw.writeCharacters("" + "(GMT+8) " + datetime);
		xmlsw.writeEndElement();
		xmlsw.writeStartElement("OS");
		xmlsw.writeCharacters("" + environment.getConfig().getOs());
		xmlsw.writeEndElement();
		xmlsw.writeStartElement("Host");
		xmlsw.writeCharacters("" + environment.getConfig().getHost());
		xmlsw.writeEndElement();
		xmlsw.writeStartElement("Port");
		xmlsw.writeCharacters("" + environment.getConfig().getPort());
		xmlsw.writeEndElement();
		xmlsw.writeStartElement("BaseUrl");
		xmlsw.writeCharacters("" + environment.getConfig().getBaseUrl());
		xmlsw.writeEndElement();
		xmlsw.writeStartElement("Browser");
		xmlsw.writeCharacters("" + environment.getConfig().getBrowser());
		xmlsw.writeEndElement();
		xmlsw.writeStartElement("Language");
		xmlsw.writeCharacters("" + environment.getConfig().getLanguage());
		xmlsw.writeEndElement();
		xmlsw.writeStartElement("Timeout");
		xmlsw.writeCharacters("" + environment.getConfig().getTimeout());
		xmlsw.writeEndElement();
		xmlsw.writeStartElement("RetryTimes");
//		xmlsw.writeCharacters("" + environment.getConfig().getRetryTimes());
		xmlsw.writeEndElement();
		xmlsw.writeStartElement("PageDataPath");
		xmlsw.writeCharacters("" + environment.getConfig().getPageDataPath());
		xmlsw.writeEndElement();
		xmlsw.writeStartElement("TestCasePath");
		xmlsw.writeCharacters("" + environment.getConfig().getTestCasePath());
		xmlsw.writeEndElement();
		xmlsw.writeEndElement();// end the configurations
	}
}
